
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Text;

using Swaf;
using Swaf.BizRule;
using Swaf.BizObj;
using Swaf.Container;
namespace CoreMiddleTier.ParallelIterator
{
    public class BizRules
    {
        /// <summary>
        /// The main calling point for clients to execute a parallel iteration.
        /// </summary>
        /// <param name="app"></param>
        /// <param name="context"></param>
        /// <param name="info"></param>
        /// <returns></returns>
        public string parallelIterate(IApplication app, IRuleContext context, IBizObj info)
        {
            ExecutionProcessor proc = new ExecutionProcessor(app, context, info);
            //Step 1: execute parallel element to get bizobj count size.
            int numBizObjs = proc.BizObjCount;
            int groupSize = Convert.ToInt32(info.get("groupSize"));

            IRuleGroup subGroup = context.RuleGroup.createChildGroup(false);
            if (numBizObjs > 0 && numBizObjs <= groupSize)
            {
                IBizObjList bizObjs = proc.BizObjList;
                for (IBizObjCursor cur = bizObjs.createCursor(); !cur.eof; cur.moveNext())
                    proc.callBizObjHandler(cur.currentElement, subGroup);
            }
            else
            {
                //Use parallel iterator
                int blockPos = 0;
                object lowKey = null;

                //Step 2: Get Ids
                IBizObjList bizObjIds = proc.BizObjIds;
                for (IBizObjCursor cur = bizObjIds.createCursor(); !cur.eof; cur.moveNext())
                {
                    if (lowKey == null)
                        lowKey = cur.currentElement.KeyValue;
                    ++blockPos;
                    if (blockPos == groupSize)
                    {
                        //Step 3 execute the processGroup rule.
                        IBizObj groupInfo = app.bizObjMgr.create("ParallelIteratorGroup");
                        groupInfo.put("lowKey", lowKey);
                        groupInfo.put("highKey", cur.currentElement.KeyValue);
                        groupInfo.put("baseInfo", info);

                        subGroup.execute(context, "parallelIterate_processGroup", null, "", groupInfo);
                        blockPos = 0;
                        lowKey = null;
                    }
                }
                //To catch the last partial block...
                if (blockPos > 0)
                {
                    IBizObj groupInfo = app.bizObjMgr.create("ParallelIteratorGroup");
                    groupInfo.put("lowKey", lowKey);
                    groupInfo.put("baseInfo", info);

                    subGroup.execute(context, "parallelIterate_processGroup", null, "", groupInfo);
                }
            }
            System.Console.WriteLine("Total rules in subGroup before wait " + subGroup.TotalRules);
            subGroup.waitForCompletion(true);
            System.Console.WriteLine("Total rules in subGroup after wait " + subGroup.TotalRules);

            return "";
        }

        public string parallelIterate_processGroup(IApplication app, IRuleContext context, IBizObj info)
        {
            try
            {
                ExecutionProcessor proc = new ExecutionProcessor(app, context, (IBizObj)info.get("baseInfo"));
                IBizObjList bizObjs = proc.getBizObjGroup(info);

                for (IBizObjCursor cur = bizObjs.createCursor(); !cur.eof; cur.moveNext())
                    proc.callBizObjHandler(cur.currentElement, context.RuleGroup);
            }
            catch (Exception e)
            {
                System.Console.WriteLine("Group:" + e.ToString());
            }
            return "";
        }

        public string parallelIterate_processAll(IApplication app, IRuleContext context, IBizObj info)
        {
            return "";
        }
    }

    /// <summary>
    ///  Maintains state about the current parallel processing and can execute the dars and bizrules specified in the parallel iteration info.
    /// </summary>
    class ExecutionProcessor
    {
        private IApplication m_app;
        private IRuleContext m_context;
        private IBizObj m_info;

        public ExecutionProcessor(IApplication app, IRuleContext context, IBizObj info)
        {
            m_app = app;
            m_context = context;
            m_info = info;
        }

        public int BizObjCount
        {
            get
            {
                try
                {
                    IBizObj countInfo = (IBizObj)m_info.get("getBizObjCount");
                    return Convert.ToInt32(execute(countInfo, null, true, null));
                }
                catch (Exception e)
                {
                    throw new ParallelIterationException("Error executing BizObjCount", e);
                }
            }
        }

        public IBizObjList BizObjIds
        {
            get
            {
                try
                {
                    IBizObj idInfo = (IBizObj)m_info.get("getBizObjIds");
                    return (IBizObjList)execute(idInfo, null, true, null);
                }
                catch (Exception e)
                {
                    throw new ParallelIterationException("Error executing BizObjIds", e);
                }
            }
        }

        public IBizObjList BizObjList
        {
            get
            {
                try
                {
                    IBizObj idInfo = (IBizObj)m_info.get("getAllBizObjs");
                    return (IBizObjList)execute(idInfo, null, true, null);
                }
                catch (Exception e)
                {
                    throw new ParallelIterationException("Error executing BizObjList", e);
                }
            }
        }

        public IBizObjList getBizObjGroup(IBizObj info)
        {
            try
            {
                IBizObj groupInfo = null;
                if (info.get("highKey").ToString() == "" && info.get("baseInfo.getLastBizObjGroup.name").ToString() != "")
                    groupInfo = (IBizObj)m_info.get("getLastBizObjGroup");
                if (groupInfo == null)
                    groupInfo = (IBizObj)m_info.get("getBizObjGroup");
                return (IBizObjList)execute(groupInfo, info, true, null);
            }
            catch(Exception e)
            {
                throw new ParallelIterationException("Error executing getBizObjGroup",e);
            }
        }

        public void callBizObjHandler(IBizObj obj, IRuleGroup grp)
        {
            try
            {
                IBizObj handlerInfo = (IBizObj)m_info.get("processBizObj");
                if (handlerInfo.get("name").ToString() != "")
                    execute(handlerInfo, obj, false, grp);
            }
            catch (Exception e)
            {
                throw new ParallelIterationException("Error executing callBizObjHandler", e);
            }
        }

        private object execute(IBizObj info, object paramInfo, bool wait, IRuleExecutor rm)
        {
            try
            {
                if (Convert.ToBoolean(info.get("isBizRule")))
                {
                    if (rm == null)
                        rm = m_app.ruleMgr;
                    if (wait)
                    {
                        IResults rc = rm.execute(m_context, info.get("name").ToString(), new object[] { paramInfo });
                        if (rc.ExceptionInfo == null)
                            return rc.CallResults;
                        else
                            throw new ParallelIterationException("Cannot get execute rule for parallel iteration", rc.ExceptionInfo);
                    }
                    else
                    {
                        return rm.execute(m_context, info.get("name").ToString(), null, "", paramInfo);
                    }
                }
                else
                {
                    object p = m_context;
                    if (paramInfo is INameAddressSupport || paramInfo is System.Collections.IDictionary)
                        p = paramInfo;
                    return m_app.appData.get(info.get("name").ToString(), p);
                }
            }
            catch (Exception e)
            {
                throw new ParallelIterationException("Error Executing execute", e);
            }
        }
    }
}
